//
//  AGLKTextureLoader.m
//  OpenGLES_Learning
//
//  Created by Mac Center on 7/30/13.
//  Copyright (c) 2013 Mac Center. All rights reserved.
//

#import "AGLKTextureLoader.h"



/////////////////////////////////////////////////////////////////
// This data type is used specify power of 2 values.  OpenGL ES
// best supports texture images that have power of 2 dimensions.
typedef enum
{
    AGLK1 = 1,
    AGLK2 = 2,
    AGLK4 = 4,
    AGLK8 = 8,
    AGLK16 = 16,
    AGLK32 = 32,
    AGLK64 = 64,
    AGLK128 = 128,
    AGLK256 = 256,
    AGLK512 = 512,
    AGLK1024 = 1024,
}
AGLKPowerOf2;

// Forward declaration of function
static AGLKPowerOf2 AGLKCalculatePowerOf2ForDimension(GLuint dimension);

/////////////////////////////////////////////////////////////////
// Forward declaration of function
static NSData *AGLKDataWithResizedCGImageBytes(
                                               CGImageRef cgImage,
                                               size_t *widthPtr,
                                               size_t *heightPtr);


@interface AGLKTextureInfo (AGLKTextureLoader)
- (id)initWithName:(GLuint)aName target:(GLenum)aTarget width:(size_t)aWidth height:(size_t)aHeight;
@end

@implementation AGLKTextureInfo
@synthesize name;
@synthesize target;
@synthesize width;
@synthesize height;

- (id)initWithName:(GLuint)aName
            target:(GLenum)aTarget
             width:(size_t)aWidth
            height:(size_t)aHeight
{
    if (nil != (self = [super init]))
    {
        name = aName;
        target = aTarget;
        width = aWidth;
        height = aHeight;
    }
    
    return self;
}



@end

@implementation AGLKTextureLoader

+ (AGLKTextureInfo *)textureWithCGImage:(CGImageRef)cgImage
                                options:(NSDictionary *)options
                                  error:(NSError **)outError {
    
    
    // Get the bytes to be used when copying data into new texture
    // buffer
    size_t width;
    size_t height;
    NSData *imageData = AGLKDataWithResizedCGImageBytes(
                                                        cgImage,
                                                        &width,
                                                        &height);
    
    // Generation, bind, and copy data into a new texture buffer
    GLuint      textureBufferID;
    
    glGenTextures(1, &textureBufferID);                  // Step 1
    glBindTexture(GL_TEXTURE_2D, textureBufferID);       // Step 2
    
    glTexImage2D(                                        // Step 3
                 GL_TEXTURE_2D,
                 0,
                 GL_RGBA,
                 width,
                 height,
                 0,
                 GL_RGBA,
                 GL_UNSIGNED_BYTE,
                 [imageData bytes]);
    
    // Set parameters that control texture sampling for the bound
    // texture
    glTexParameteri(GL_TEXTURE_2D,
                    GL_TEXTURE_MIN_FILTER,
                    GL_LINEAR); 
    
    // Allocate and initialize the AGLKTextureInfo instance to be
    // returned
    AGLKTextureInfo *result = [[AGLKTextureInfo alloc] 
                               initWithName:textureBufferID
                               target:GL_TEXTURE_2D
                               width:width
                               height:height];
    
    return result;
}


static NSData *AGLKDataWithResizedCGImageBytes( CGImageRef cgImage,
                                               size_t *widthPtr,
                                               size_t *heightPtr)
{
    NSCParameterAssert(NULL != cgImage);
    
    NSCParameterAssert(widthPtr != NULL);
    NSCParameterAssert(heightPtr != NULL);
    
    size_t  originalWidth = CGImageGetWidth(cgImage);
    size_t originalHeight = CGImageGetHeight(cgImage);
    
    NSCAssert(0 < originalWidth, @"Invalid image width");
    NSCAssert(0 < originalHeight, @"Invalid image width");
    
    size_t width = AGLKCalculatePowerOf2ForDimension(originalWidth);
    size_t height = AGLKCalculatePowerOf2ForDimension(originalHeight);
    
    NSMutableData *imageData = [NSMutableData dataWithLength: height * width * 4]; // 4 bytes per RGBA pixel
    
    NSCAssert(nil != imageData, @"Unable to allocate image storage");
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef cgContext = CGBitmapContextCreate([imageData mutableBytes], width, height, 8, 4 * width, colorSpace, kCGImageAlphaPremultipliedLast);
    CGColorSpaceRelease(colorSpace);
    
    // Flip the Core Graphics Y-axis for future drawing
    CGContextTranslateCTM (cgContext, 0, height);
    CGContextScaleCTM (cgContext, 1.0, -1.0);
    
    CGContextDrawImage(cgContext, CGRectMake(0, 0, width, height), cgImage);
    CGContextRelease(cgContext);
    *widthPtr = width;
    *heightPtr = height;
    
    return imageData;
}

/////////////////////////////////////////////////////////////////
// This function calculates and returns the nearest power of 2
// that is greater than or equal to the dimension argument and
// less than or equal to 1024.
static AGLKPowerOf2 AGLKCalculatePowerOf2ForDimension(GLuint dimension)
{
    AGLKPowerOf2  result = AGLK1;
    
    if(dimension > (GLuint)AGLK512)
    {
        result = AGLK1024;
    }
    else if(dimension > (GLuint)AGLK256)
    {
        result = AGLK512;
    }
    else if(dimension > (GLuint)AGLK128)
    {
        result = AGLK256;
    }
    else if(dimension > (GLuint)AGLK64)
    {
        result = AGLK128;
    }
    else if(dimension > (GLuint)AGLK32)
    {
        result = AGLK64;
    }
    else if(dimension > (GLuint)AGLK16)
    {
        result = AGLK32;
    }
    else if(dimension > (GLuint)AGLK8)
    {
        result = AGLK16;
    }
    else if(dimension > (GLuint)AGLK4)
    {
        result = AGLK8;
    }
    else if(dimension > (GLuint)AGLK2)
    {
        result = AGLK4;
    }
    else if(dimension > (GLuint)AGLK1)
    {
        result = AGLK2;
    }
    
    return result;
}
@end
